package org.huzhp.gateway;/******************************************************************************
 * Copyright (C) 2019 Guangzhou Mayin Environmental Technology Co.,Ltd
 * All Rights Reserved.
 * 本软件为广州美盈开发研制。未经本公司正式书面同意，其他任何个人、团体不得使用、
 * 复制、修改或发布本软件.
 * This software is developed for Guangzhou Mayin. No other individual or group may use, copy,
 * modify or distribute the Software without the official written consent of the Company.
 *****************************************************************************/

import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import com.netflix.config.ConfigurationManager;
import com.netflix.config.DeploymentContext;
import com.netflix.loadbalancer.*;

import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @Description: java类作用描述
 * @Author: huzp
 * @CreateDate: 2020/4/16 9:43
 * @Version: 1.0
 */
public class TestNetflix {

    public static void main(String[] args) throws InterruptedException {
        String clientName = "YourBatman";
        // 负载均衡器状态信息   后面模拟请求来增加指标数据
        LoadBalancerStats lbs = new LoadBalancerStats(clientName);

        // 添加Server
        List<Server> serverList = new ArrayList<>();
//        serverList.add(createServer("华南", 1));
//        serverList.add(createServer("华东", 1));
//        serverList.add(createServer("华东", 2));

        serverList.add(createServer("华北", 1));
        serverList.add(createServer("华北", 2));
//        serverList.add(createServer("华北", 3));
//        serverList.add(createServer("华北", 4));
        lbs.updateServerList(serverList);

        Map<String, List<Server>> zoneServerMap = new HashMap<>();
        // 模拟向每个Server发送请求  记录ServerStatus数据
        serverList.forEach(server -> {
            ServerStats serverStat = lbs.getSingleServerStat(server);
            request(serverStat);

            // 顺便按照zone分组
            String zone = server.getZone();
            if (zoneServerMap.containsKey(zone)) {
                zoneServerMap.get(zone).add(server);
            } else {
                List<Server> servers = new ArrayList<>();
                servers.add(server);
                zoneServerMap.put(zone, servers);
            }
        });
        lbs.updateZoneServerMapping(zoneServerMap);

        // 指定当前的zone
//        DeploymentContext deploymentContext = ConfigurationManager.getDeploymentContext();
//        deploymentContext.setValue(DeploymentContext.ContextKey.zone, "华北");
        // 准备一个服务列表过滤器
        IClientConfig config = DefaultClientConfigImpl.getClientConfigWithDefaultValues(clientName);
        config.set(CommonClientConfigKey.EnableZoneAffinity, true);
        ZoneAffinityServerListFilter serverListFilter = new ZoneAffinityServerListFilter();
        serverListFilter.setLoadBalancerStats(lbs);
        serverListFilter.initWithNiwsConfig(config);

        // 从lbs里拿到一些监控数据
        monitor(lbs, serverListFilter);

        TimeUnit.SECONDS.sleep(500);

    }

    private static void monitor(LoadBalancerStats lbs, ZoneAffinityServerListFilter serverListFilter) {
        List<String> zones = Arrays.asList("华南", "华东", "华北");
        new Thread(() -> {
            ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
            executorService.scheduleWithFixedDelay(() -> {
                // 打印当前可用区
                // 获取可用区
                Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(lbs, 0.2d, 0.99999d);
                System.out.println("=====当前可用区为：" + availableZones);

                List<Server> filteredListOfServers = serverListFilter.getFilteredListOfServers(new ArrayList(lbs.getServerStats().keySet()));
                System.out.println("=====过滤后可用的服务列表：" + filteredListOfServers);

                zones.forEach(zone -> {
                    System.out.printf("区域[" + zone + "]概要：");
                    int instanceCount = lbs.getInstanceCount(zone);
                    int activeRequestsCount = lbs.getActiveRequestsCount(zone);
                    double activeRequestsPerServer = lbs.getActiveRequestsPerServer(zone);
                    // ZoneSnapshot zoneSnapshot = lbs.getZoneSnapshot(zone);

                    System.out.printf("实例总数：%s，活跃请求总数：%s，平均负载：%s\n", instanceCount, activeRequestsCount, activeRequestsPerServer);
                    // System.out.println(zoneSnapshot);
                });
                System.out.println("======================================================");
            }, 5, 5, TimeUnit.SECONDS);
        }).start();
    }

    // 请注意：请必须保证Server的id不一样，否则放不进去List的（因为Server的equals hashCode方法仅和id有关）
// 所以此处使用index作为port，以示区分
    private static Server createServer(String zone, int index) {
        Server server = new Server("www.baidu" + zone + ".com", index);
        server.setZone(zone);
        return server;
    }


    // 多线程，模拟请求
    private static void request(ServerStats serverStats) {
        new Thread(() -> {
            // 每10ms发送一个请求（每个请求处理10-200ms的时间），持续不断
            ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
            executorService.scheduleWithFixedDelay(() -> {
                new Thread(() -> {
                    // 请求之前 记录活跃请求数
                    serverStats.incrementActiveRequestsCount();
                    serverStats.incrementNumRequests();
                    long rt = doSomething();
                    // 请求结束， 记录响应耗时
                    serverStats.noteResponseTime(rt);
                    serverStats.decrementActiveRequestsCount();
                }).start();
            }, 10, 10, TimeUnit.MILLISECONDS);
        }).start();
    }

    // 模拟请求耗时，返回耗时时间
    private static long doSomething() {
        try {
            int rt = randomValue(10, 200);
            TimeUnit.MILLISECONDS.sleep(rt);
            return rt;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return 0L;
        }
    }

    // 本地使用随机数模拟数据收集
    private static int randomValue(int min, int max) {
        return min + (int) (Math.random() * ((max - min) + 1));
    }

}
